use clap::Parser;
use anyhow::{Result, Context, Error};
use image::GenericImageView;
use nom::{
    branch::alt, bytes::complete::tag, number::complete::double, IResult
};
#[derive(Parser)]
struct Cli {
    #[arg(help = "源图片路径")]
    source:String,
    #[arg(value_parser(parse_image_size), help = "最大尺寸检查，格式为<width>x<height> 当图片尺寸超过最大尺寸时，会报错", long)]
    max_size:Option<(u32, u32)>,
    #[arg(long, help = "文件合法性检查，当文件后缀与文件数据不匹配时，会报错", default_value_t = false)]
    file_check:bool,
    #[arg(long, help = "PNG透明通道检查，当PNG不包含透明通道时，会报错", default_value_t = false)]
    png_alpha_channel_check:bool,
    #[arg(long, help = "最大文件大小检查，当文件大小超过最大文件大小时，会报错，单位支持b、k(b)、m(b)、g(b)、t(b)，支持小数，如3.5mb", value_parser(file_size_parser))]
    max_file_size:Option<usize>
}

struct ImageData {
    image: image::DynamicImage,
    format: image::ImageFormat,
}

fn main() {
    let args = Cli::parse();
    let img = load_image(&args).unwrap();
    check_image(&img, &args).unwrap();
}

fn load_image(cli: &Cli) -> Result<ImageData> {
    //首先检查文件大小
    if let Some(max_file_size) = cli.max_file_size {
        let metadata = std::fs::metadata(cli.source.clone()).with_context(|| format!("failed to get metadata of image file {}", cli.source))?;
        if metadata.len() > max_file_size as u64 {
            return Err(Error::msg(format!("image file size {} exceeds the maximum file size {}", metadata.len(), max_file_size)));
        }
    }

    let img = if cli.file_check {
        image::open(cli.source.clone()).with_context(|| format!("failed to open image file {}", cli.source))?
    } else {
        let binary_data = std::fs::read(cli.source.clone()).with_context(|| format!("failed to read image file {}", cli.source))?;
        image::load_from_memory(&binary_data).with_context(|| format!("failed to load image from memory {}", cli.source))?
    };
    let format = image::ImageFormat::from_path(cli.source.clone()).with_context(|| format!("failed to get image format of {}", cli.source))?;
    Ok(ImageData { image: img, format })
}

fn check_image(img: &ImageData, cli: &Cli) -> Result<()> {
    if let Some((max_width, max_height)) = cli.max_size {
        let (width, height) = img.image.dimensions();
        if width > max_width || height > max_height {
            return Err(Error::msg(format!("image size {}x{} exceeds the maximum size {}x{}", width, height, max_width, max_height)));
        }
    }

    if cli.png_alpha_channel_check && img.format == image::ImageFormat::Png {
        if img.image.color().has_alpha() == false {
            return Err(Error::msg("PNG image does not contain an alpha channel"));
        }
    }
    
    Ok(())
}

fn parse_image_size(size: &str) -> Result<(u32, u32), String> {
    let parts: Vec<&str> = size.split('x').collect();
    if parts.len() != 2 {
        return Err("size must be in the format <width>x<height>".to_string());
    }
    let width = parts[0].parse::<u32>().map_err(|e| e.to_string())?;
    let height = parts[1].parse::<u32>().map_err(|e| e.to_string())?;
    Ok((width, height))
}

fn size_parser(input: &str) -> IResult<&str, f64> {
    // 解析数字部分
    let (input, num) = double(input)?;
    
    // 解析单位部分
    let (input, unit) = alt((
        tag("kb"), tag("k"),
        tag("mb"), tag("m"),
        tag("gb"), tag("g"),
        tag("tb"), tag("t"),
        tag("b"),
    ))(input)?;
    

    // 根据单位计算倍率
    let multiplier = match unit {
        "b" => 1.0,      // 字节
        "k" | "kb" => 1024.0, // 千字节
        "m" | "mb" => 1024.0 * 1024.0, // 兆字节
        "g" | "gb" => 1024.0 * 1024.0 * 1024.0, // 吉字节
        "t" | "tb" => 1024.0 * 1024.0 * 1024.0 * 1024.0, // 太字节
        _ => return Err(nom::Err::Error(nom::error::Error::new(input, nom::error::ErrorKind::Tag))),
    };

    // 返回计算结果
    Ok((input, num * multiplier))
}

fn file_size_parser(size: &str) -> Result<usize, String> {
    match size_parser(size) {
        Ok((_, bytes)) => Ok(bytes.round() as usize),
        Err(_) => Err("Failed to parse file size.".to_string()),
    }
}
